package main

import (
	"fmt"
	"strconv"
	"strings"
)

func main() {
	// 声明变量 go语言中变量必须在声明后使用，否则在编译期间就会报变量没有使用的错误
	var i int = 10
	// go语言会自动推导字面量的类型，因此不显示写明变量的类型也是可以的
	var a = 10
	fmt.Println(i, a)

	// 同时声明多个变量
	var (
		a1 = 1
		a2 = 2
		a3 = 3
	)
	fmt.Println(a1, a2, a3)

	// int 类型
	/**
	在 Go 语言中，整型分为：

	有符号整型：如 int、int8、int16、int32 和 int64。

	无符号整型：如 uint、uint8、uint16、uint32 和 uint64。

	它们的差别在于，有符号整型表示的数值可以为负数、零和正数，而无符号整型只能为零和正数。
	除了有用“位”（bit）大小表示的整型外，还有 int 和 uint 这两个没有具体 bit 大小的整型，它们的大小可能是 32bit，也可能是 64bit，和硬件设备 CPU 有关。

	在整型中，如果能确定 int 的 bit 就选择比较明确的 int 类型，因为这会让你的程序具备很好的移植性。

	在 Go 语言中，还有一种字节类型 byte，它其实等价于 uint8 类型，可以理解为 uint8 类型的别名，用于定义一个字节，所以字节 byte 类型也属于整型。
	*/
	var b1 int8 = 1
	var b2 int16 = -1
	var b3 uint8 = 8
	fmt.Println(b1, b2, b3)

	// 浮点数
	/**
	浮点数就代表现实中的小数。Go 语言提供了两种精度的浮点数，分别是 float32 和 float64。项目中最常用的是 float64，因为它的精度高，浮点计算的结果相比 float32 误差会更小。

	下面的代码示例定义了两个变量 f32 和 f64，它们的类型分别为 float32 和 float64。
	*/
	var f32 float32 = 2.2
	var f64 float64 = 10.3456
	fmt.Println("f32 is", f32, ",f64 is", f64)

	// 布尔值
	/**
	一个布尔型的值只有两种：true 和 false，它们代表现实中的“是”和“否”。它们的值会经常被用于一些判断中，比如 if 语句（以后的课时会详细介绍）等。Go 语言中的布尔型使用关键字 bool 定义。
	*/
	var bf bool = false
	var bt bool = true
	fmt.Println("bf is", bf, ",bt is", bt)

	// 字符串
	var s1 string = "Hello"
	var s2 string = "世界"
	fmt.Println("s1 is", s1, ",s2 is", s2)
	fmt.Println("s1+s2=", s1+s2)

	// 各个基础类型的初始值也被称为零值
	var zi int     // 0
	var zf float64 // 0
	var zb bool    // false
	var zs string  // ""
	fmt.Println(zi, zf, zb, zs)

	// 变量的简短声明
	i1 := 10
	bf1 := true
	s11 := "Hello"
	fmt.Println(i1, bf1, s11)

	// 指针
	/**
	在 Go 语言中，指针对应的是变量在内存中的存储位置，也就说指针的值就是变量的内存地址。通过 & 可以获取一个变量的地址，也就是指针。
	在以下的代码中，pi 就是指向变量 i 的指针。要想获得指针 pi 指向的变量值，通过*pi这个表达式即可。尝试运行这段程序，会看到输出结果和变量 i 的值一样。
	*/
	pi := &i                           // pi 为变量i的指针
	fmt.Println("指针", i, pi, *pi, *&i) // *pi表示 指针pi所指向的值 所以 *&i = i

	// 变量的赋值修改
	i = 20
	fmt.Println(i)

	// 常量 常量定义后可以不马上使用也可以编译通过，这点和变量不同
	// 在 Go 语言中，只允许布尔型、字符串、数字类型这些基础类型作为常量。
	const name = "clearlove"

	// iota
	/**
	iota 是一个常量生成器，它可以用来初始化相似规则的常量，避免重复的初始化。假设我们要定义 one、two、three 和 four 四个常量，对应的值分别是 1、2、3 和 4，如果不使用 iota，则需要按照如下代码的方式定义：
	*/
	//const(
	//	one = 1
	//	two = 2
	//	three =3
	//	four =4
	//)

	// 等价于
	const (
		one = iota + 1
		two
		three
		four
	)
	fmt.Println(one, two, three, four)

	// 字符串
	// 字符串和数字互转
	// int转字符串
	i2s := strconv.Itoa(i)
	// 字符串转int
	s2i, err := strconv.Atoi(i2s)
	fmt.Println(i2s, s2i, err)

	/**
	同理对于浮点数、布尔型，Go 语言提供了 strconv.ParseFloat、strconv.ParseBool、strconv.FormatFloat 和 strconv.FormatBool 进行互转，你可以自己试试。
	*/
	parseBool, err := strconv.ParseBool("true")
	fmt.Println(parseBool, err, strconv.FormatBool(true))

	// 注意 parseBool 已经是自动推导的bool类型了，所以这里对于float类型的赋值不能再使用 parseBool
	parseFloat, err := strconv.ParseFloat("12.34567", 64)
	fmt.Println(parseFloat, err, strconv.FormatFloat(12.34567, 'E', -1, 64), strconv.FormatFloat(12.34567, 'E', -1, 32))

	fmt.Println(float64(1.24532325))
	f64l := float64(5.7)
	fmt.Println(int(f64l)) // 可以这样写
	//fmt.Println(int(float64(5.7))) // 报错
	//fmt.Println(int(float32(11.2323))) // 报错
	//fmt.Println(int(float64(4324.5632))) //报错
	//fmt.Println(int(2.2222)) // 报错

	// strings 包
	/**
	讲到基础类型，尤其是字符串，不得不提 Go SDK 为我们提供的一个标准包 strings。它是用于处理字符串的工具包，里面有很多常用的函数，帮助我们对字符串进行操作，比如查找字符串、去除字符串的空格、拆分字符串、判断字符串是否有某个前缀或者后缀等。掌握好它，有利于我们的高效编程。
	以下代码是我写的关于 strings 包的一些例子，你自己可以根据strings 文档自己写一些示例，多练习熟悉它们。
	*/
	//判断s1的前缀是否是H
	fmt.Println(s1)
	fmt.Println(strings.HasPrefix(s1, "H"))
	//在s1中查找字符串o
	fmt.Println(strings.Index(s1, "o"))
	//把s1全部转为大写
	fmt.Println(strings.ToUpper(s1))
	num := strings.Index("clearlove", "l")
	fmt.Println(num)

}
